using System;
using System.Collections.Generic;
using System.Text;
using UvsChess;
using System.Linq;

namespace StudentAI
{
    public class StudentAI : IChessAI
    {
        #region IChessAI Members that are implemented by the Student
        private MoveGenerator2 moveGenerator = new MoveGenerator2();
        /// <summary>
        /// The name of your AI
        /// </summary>
        public string Name
        {
#if DEBUG
            get { return "Awesomes (Debug)"; }
#else
            get { return "Awesomes"; }
#endif
        }
        /// <summary>
        /// Well check to to see if the king is in check. It will do this based on the color that is passed in.
        /// </summary>
        /// <param name="board"></param>
        /// <param name="color"></param>
        /// <returns></returns>
        private bool IsKingIncheck(ChessBoard board, ChessColor color)
        {
            List<ChessMove> opMoves = CreatMoves(board, color == ChessColor.White ? ChessColor.Black : ChessColor.White);
            return opMoves.Any(move => move.Flag == ChessFlag.Check);
        }

        private Node Greedy(ChessBoard board, ChessColor color)
        {
            List<ChessMove> moves;
            if (IsKingIncheck(board, color))
            {
                //return new Node(null,0);
            }
            
            {
                moves = CreatMoves(board, color);
                PriQueue queue = EvaluateMoves(board, moves, color);
                return queue.Pop();
            }
            

        }

        private List<ChessMove> CreatMoves(ChessBoard board, ChessColor color)
        {
            List<ChessMove> moves = new List<ChessMove>(40);
            if (color == ChessColor.Black)
            {
                for (int y = 0; y < ChessBoard.NumberOfRows; y++)
                {
                    for (int x = 0; x < ChessBoard.NumberOfColumns; x++)
                    {
                        if (color == ChessColor.White)
                        {
                            if (board[x, y] > ChessPiece.Empty)
                            {
                                var move = moveGenerator.GenerateMoves(board, x, y, color);
                                if (move != null)
                                {
                                    moves.AddRange(move);
                                }
                            }
                        }
                        else if (color == ChessColor.Black)
                        {
                            if (board[x, y] < ChessPiece.Empty)
                            {
                                var move = moveGenerator.GenerateMoves(board, x, y, color);
                                if (move != null)
                                {
                                    moves.AddRange(move);
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                for (int y = ChessBoard.NumberOfRows - 1; y >= 0; y--)
                {
                    for (int x = ChessBoard.NumberOfColumns - 1; x >= 0; x--)
                    {
                        if (color == ChessColor.White)
                        {
                            if (board[x, y] > ChessPiece.Empty)
                            {
                                var move = moveGenerator.GenerateMoves(board, x, y, color);
                                if (move != null)
                                {
                                    moves.AddRange(move);
                                }
                            }
                        }
                        else if (color == ChessColor.Black)
                        {
                            if (board[x, y] < ChessPiece.Empty)
                            {
                                var move = moveGenerator.GenerateMoves(board, x, y, color);
                                if (move != null)
                                {
                                    moves.AddRange(move);
                                }
                            }
                        }
                    }
                }
            }

            return moves;
        }

        private PriQueue EvaluateMoves(ChessBoard board, List<ChessMove> moves, ChessColor color )
        {
            PriQueue queue = new PriQueue();
            
            foreach (ChessMove move in moves)
            {
                var node = Evaluate(board, move, color);
                if( node != null)
                {
                    queue.Apppend(node);
                }
            }
            return queue;
        }

        private Node Evaluate( ChessBoard board, ChessMove move, ChessColor color)
        {
            if(move.Flag == ChessFlag.Check )
            {
                return null;
            }
            int pri = Heuristic.GetPriority(board, move, color);
            var clone = board.Clone();
            clone.MakeMove(move);
            var moves = CreatMoves(clone, color);
            var check = moves.FindAll(m => m.Flag == ChessFlag.Check);
            if(check.Count > 0)
            {
                //
               
                    if (CheckMate(clone, color == ChessColor.White ? ChessColor.Black : ChessColor.White))
                    {
                        move.Flag = ChessFlag.Checkmate;
                        pri += 20;
                    }
                    else
                    {
                        move.Flag = ChessFlag.Check;
                    }
            }
            if(IsKingIncheck(clone, color))
            {
                return null;
            }
            return new Node(move,pri);
           
        }
        
        private bool CheckMate(ChessBoard board, ChessColor color)
        {
            var moves = CreatMoves(board, color);
            bool checkMate = true;
            foreach (ChessMove chessMove in moves)
            {
                ChessBoard clone = board.Clone();
                clone.MakeMove(chessMove);
                checkMate &= IsKingIncheck(clone, color);
            }
            return checkMate;
        }

        /// <summary>
        /// Evaluates the chess board and decided which move to make. This is the main method of the AI.
        /// The framework will call this method when it's your turn.
        /// </summary>
        /// <param name="board">Current chess board</param>
        /// <param name="yourColor">Your color</param>
        /// <returns> Returns the best chess move the player has for the given chess board</returns>
        public ChessMove GetNextMove(ChessBoard board, ChessColor myColor)
        {
            Node node = Greedy(board, myColor);
            return node.Move;
        }

        /// <summary>
        /// Validates a move. The framework uses this to validate the opponents move.
        /// </summary>
        /// <param name="boardBeforeMove">The board as it currently is _before_ the move.</param>
        /// <param name="moveToCheck">This is the move that needs to be checked to see if it's valid.</param>
        /// <param name="colorOfPlayerMoving">This is the color of the player who's making the move.</param>
        /// <returns>Returns true if the move was valid</returns>
        public bool IsValidMove(ChessBoard boardBeforeMove, ChessMove moveToCheck, ChessColor colorOfPlayerMoving)
        {
            var moves = moveGenerator.GenerateMoves(boardBeforeMove, moveToCheck.From.X, moveToCheck.From.Y,
                                                    colorOfPlayerMoving);
            foreach (ChessMove chessMove in moves)
            {
                if(chessMove.To == moveToCheck.To)
                {
                    var node = Evaluate(boardBeforeMove, chessMove, colorOfPlayerMoving);
                    if(node.Move == moveToCheck)
                    {
                        return true;
                    }
                    return true;
                }
            }
            return false;
        }

        #endregion
















        #region IChessAI Members that should be implemented as automatic properties and should NEVER be touched by students.
        /// <summary>
        /// This will return false when the framework starts running your AI. When the AI's time has run out,
        /// then this method will return true. Once this method returns true, your AI should return a 
        /// move immediately.
        /// 
        /// You should NEVER EVER set this property!
        /// This property should be defined as an Automatic Property.
        /// This property SHOULD NOT CONTAIN ANY CODE!!!
        /// </summary>
        public AIIsMyTurnOverCallback IsMyTurnOver { get; set; }

        /// <summary>
        /// Call this method to print out debug information. The framework subscribes to this event
        /// and will provide a log window for your debug messages.
        /// 
        /// You should NEVER EVER set this property!
        /// This property should be defined as an Automatic Property.
        /// This property SHOULD NOT CONTAIN ANY CODE!!!
        /// </summary>
        /// <param name="message"></param>
        public AILoggerCallback Log { get; set; }

        /// <summary>
        /// Call this method to catch profiling information. The framework subscribes to this event
        /// and will print out the profiling stats in your log window.
        /// 
        /// You should NEVER EVER set this property!
        /// This property should be defined as an Automatic Property.
        /// This property SHOULD NOT CONTAIN ANY CODE!!!
        /// </summary>
        /// <param name="key"></param>
        public AIProfiler Profiler { get; set; }

        /// <summary>
        /// Call this method to tell the framework what decision print out debug information. The framework subscribes to this event
        /// and will provide a debug window for your decision tree.
        /// 
        /// You should NEVER EVER set this property!
        /// This property should be defined as an Automatic Property.
        /// This property SHOULD NOT CONTAIN ANY CODE!!!
        /// </summary>
        /// <param name="message"></param>
        public AISetDecisionTreeCallback SetDecisionTree { get; set; }
        #endregion
    }
}
